from Box2D import b2_dynamicBody, b2Vec2
from Specials import Meteor
from libavg import avg, ui
from random import randint
import Constants
import Level
import math

class Shuttle(avg.DivNode):
    
    __BONUS_INVINCIBLE = False
    __BONUS_SLIME = False
    __BONUS_FIRE = False
    
    __minusPoints = 0
    __isInvincible = False
    __inRepairMode = False
    
    def __init__(self, parent, position, size=None, **kwargs):
        super(Shuttle, self).__init__(**kwargs)

        #Add this shuttle to the parent actor node        
        if parent:
            parent.appendChild(self)
            self.__actor = parent
        
        w = Constants.SHUTTLE_SIZE_DEFAULT.x
        h = Constants.SHUTTLE_SIZE_DEFAULT.y

        self.__livePoints = Constants.SHUTTLE_LIVEPOINTS
        self.__lives = Constants.SHUTTLE_LIVES
        self.__bonusTimer = None
        self.__ignoreScale = True
        self.__ignoreRotation = False
        self.__repairTimer = None

        self.pos = position
        if size is not None:
            self.size = size
        else:
            self.size = (w, h)
        
        #sounds
        self.__redAlert = avg.SoundNode(parent=self, href="resources/sound/red_alert.mp3", loop=False, volume=Constants.SHUTTLE_VOL)
        self.__redAlertPlayed = False
        self.__destrcution = avg.SoundNode(parent=self, href="resources/sound/explosion.mp3", loop=False, volume=Constants.SHUTTLE_VOL)
        self.__asteroidSound = avg.SoundNode(parent=self, href="resources/sound/meteor.mp3", loop=False, volume=Constants.METEOR_VOL) 
        
        #Graphics
        self.__centerRect = avg.RectNode(fillopacity=0, pos=(0, w / 2), size=(w, h - w), parent=self, strokewidth=0)
        self.__leftCircle = avg.CircleNode(pos=(w / 2, w / 2), r= w / 2, fillopacity=0, parent=self, strokewidth=0)
        self.__rightCircle = avg.CircleNode(pos=(w / 2, h - w / 2), r= w / 2, fillopacity=0, parent=self, strokewidth=0)
        self.__sImg = avg.ImageNode(href = "resources/textures/shuttle/player"+ (self.__actor.getActorID()).__str__() +"_default.png", size = (w, h), parent = self)
        self.__sImgBonus = avg.ImageNode(size = (w, h), parent=self)
        if (self.__actor.getActorID() == 1):
            self.__sImgBonus.angle = math.pi
        
        # rectangle
        b1 = self.__actor.getLevel().world.Rectangle(position=(self.__actor.pos.x + self.pos.x + self.__centerRect.pos.x, self.__actor.pos.y + self.pos.y + self.__centerRect.pos.y), size=(w, h - w))
        b1.bullet = True
        b1.userData = self
        b1.fixtures[0].filterData.maskBits = 248
        b1.fixtures[0].filterData.categoryBits = 4 if self.__actor.getActorID() == 0 else 8
        
        # left circle
        b2 = self.__actor.getLevel().world.Circle(position=(self.__actor.pos.x + self.pos.x + self.__centerRect.pos.x + w/2, self.__actor.pos.y + self.pos.y + self.__centerRect.pos.y), radius=w / 2)
        b2.bullet = True
        b2.userData = self
        b2.fixtures[0].filterData.maskBits = 248
        b2.fixtures[0].filterData.categoryBits = 4 if self.__actor.getActorID() == 0 else 8
        # right circle
        b3 = self.__actor.getLevel().world.Circle(position=(self.__actor.pos.x + self.pos.x + self.__centerRect.pos.x + w/2, self.__actor.pos.y + self.pos.y + self.__centerRect.pos.y + self.__centerRect.size.y), radius=w / 2)
        b3.bullet = True
        b3.userData = self
        b3.fixtures[0].filterData.maskBits = 248
        b3.fixtures[0].filterData.categoryBits = 4 if self.__actor.getActorID() == 0 else 8
        self.__body = (b1, b2, b3)
        
        if (self.__actor.getActorID() == 0):
            self.__liveBeam = avg.RectNode(pos = (self.__actor.pos.x + 10, self.__actor.pos.y + 25), fillcolor="00FF00", fillopacity=1, color="6b6b68", size = (Constants.SHUTTLE_SIZE_DEFAULT.x - 30, Constants.SHUTTLE_SIZE_DEFAULT.y - 50), parent=self)
            
            self.__pointsNode = avg.DivNode(pos = (self.__actor.pos.x + 35, self.__actor.pos.y + 30), parent = self)
            self.__pointsText = avg.WordsNode(color="000000", variant="Bold", text="Points: ", parent=self.__pointsNode)
            self.__points = avg.WordsNode(pos = (self.__pointsText.pos.x + self.__pointsText.size.x, self.__pointsText.pos.y), variant="Bold", color="000000",  text="0", parent=self.__pointsNode)
            self.__pointsNode.angle = 0.5 * math.pi
        
            self.__livePointsNode = avg.DivNode(pos = (self.__pointsNode.pos.x, self.__pointsNode.pos.y + 120), parent = self)
            self.__livePointsText = avg.WordsNode(color="000000", variant="Bold", text="Life: ", parent=self.__livePointsNode)
            self.__liveText = avg.WordsNode(pos = (self.__livePointsText.pos.x + self.__livePointsText.size.x, self.__livePointsText.pos.y), variant="Bold", color="000000",  text=Constants.SHUTTLE_LIVES.__str__() + " / 3", parent=self.__livePointsNode)
            self.__livePointsNode.angle = 0.5 * math.pi

        else:
            self.__liveBeam = avg.RectNode(pos = (20, self.__actor.pos.y + 25), fillcolor="00FF00", fillopacity=1, color="6b6b68", size = (Constants.SHUTTLE_SIZE_DEFAULT.x - 30, Constants.SHUTTLE_SIZE_DEFAULT.y - 50), parent=self)
            
            self.__pointsNode = avg.DivNode(pos = (25, self.__actor.pos.y + Constants.SHUTTLE_SIZE_DEFAULT.y - 30), parent = self)
            self.__pointsText = avg.WordsNode(color="000000", variant="Bold", text="Points: ", parent=self.__pointsNode)
            self.__points = avg.WordsNode(pos = (self.__pointsText.pos.x + self.__pointsText.size.x, self.__pointsText.pos.y), variant="Bold", color="000000",  text="0", parent=self.__pointsNode)
            self.__pointsNode.angle = -0.5 * math.pi
            
            self.__livePointsNode = avg.DivNode(pos = (self.__pointsNode.pos.x, self.__pointsNode.pos.y - 120), parent = self)
            self.__livePointsText = avg.WordsNode(color="000000", variant="Bold", text="Life: ", parent=self.__livePointsNode)
            self.__liveText = avg.WordsNode(pos = (self.__livePointsText.pos.x + self.__livePointsText.size.x, self.__livePointsText.pos.y), variant="Bold", color="000000",  text=Constants.SHUTTLE_LIVES.__str__() + " / 3", parent=self.__livePointsNode)
            self.__livePointsNode.angle = -0.5 * math.pi

        self.__recognizer = ui.TransformRecognizer(
                eventNode=self, 
                detectedHandler=self.__onDetected,
                moveHandler=self.__onMove,
                upHandler=self.__onUp,
                friction=0.05
        )
        
        self.__onFrameHandler = avg.Player.get().setOnFrameHandler(self.__update)
        
    def getActor(self):
        return self.__actor.getActorID()     
        
    def __update(self):
        if (self.__livePoints > 0):
            self.__updateLiveBeam()
        elif self.__inRepairMode == False:
            self.kill()
        
        if self.__BONUS_INVINCIBLE == True:
            self.__BONUS_INVINCIBLE = False
            self.__sImgBonus.href = "resources/textures/shuttle/iron.png"
            if self.__isInvincible == True:
                avg.Player.get().clearInterval(self.__bonusTimer)
            self.__isInvincible = True
            self.__bonusTimer = avg.Player.get().setInterval(5000, self.__bonusTimeout)    
        elif self.__BONUS_SLIME == True:
            self.__BONUS_SLIME = False
            self.__sImgBonus.href = "resources/textures/shuttle/slime.png"
            self.__bonusTimer = avg.Player.get().setInterval(5000, self.__bonusTimeout)
        elif self.__BONUS_FIRE == True:
            self.__BONUS_FIRE = False
            self.__sImgBonus.href = "resources/textures/shuttle/fire.png"
            self.__bonusTimer = avg.Player.get().setInterval(5000, self.__bonusTimeout)
        
    def setPoints(self, points):
        self.__points.text = points.__str__()

    def onBeginContact(self, o):
        pass
        
    def onEndContact(self, o):
        if self.__inRepairMode:
            return
        
        elif(isinstance(o, Ball)):
            
            enemyRepair = False
            if self.__actor.getLevel().actors.__len__() > 1:
                enemyRepair = self.__actor.getLevel().actors[1-self.__actor.getActorID()].shuttles[0].inRepairMode == True

            if o.isFrozen() == True or enemyRepair:
                return
            
            if(o.getActor() != self.__actor.getActorID()):
                self.decreaseLive(Constants.SHUTTLE_ENEMY_BALL_COLLISION)
                if (self.__actor.getLevel().actors.__len__() > 1):
                    self.__actor.getLevel().actors[o.getActor()].increasePoints(Constants.SHUTTLE_ENEMY_BALL_COLLISION_PLUS)
        
        elif isinstance(o, Meteor):
            self.decreaseLive(Constants.SHUTTLE_ASTEROID_COLLISION)
            self.__asteroidSound.stop()
            self.__asteroidSound.play()
                
    def decreaseLive(self, live):
        if self.__isInvincible == True or self.__inRepairMode:
            return
        self.__livePoints -= live

    def kill(self):
        self.__lives -= 1
        if self.__lives > 0:
            if (self.__actor.getLevel().actors.__len__() > 1):
                self.__actor.getLevel().actors[1-self.__actor.getActorID()].increasePoints(Constants.SHUTTLE_DESTROY)
                self.__livePoints = 0
                self.__destrcution.play()
                self.__updateLiveBeam()
                self.__inRepairMode = True
                self.__sImg.size = (100, 250)
                self.__sImg.href = "resources/textures/shuttle/player"+ (self.__actor.getActorID()).__str__() +"_repair.png"
                self.__repairTimer = avg.Player.get().setInterval(50, self.__repair)
                for b in self.__actor.getLevel().balls:
                    if b.getActor() == self.__actor.getActorID():
                        b.pauseBall();
            self.__liveText.text = self.__lives.__str__() + " / 3"
        else:
            self.__liveText.text = "dead"
            self.__actor.DestroyShuttle(self)
                
    def __repair(self):
        self.__livePoints += 2
        
        if (self.__livePoints >= Constants.SHUTTLE_LIVEPOINTS - ((Constants.SHUTTLE_LIVES - self.__lives) * 20)):
            self.__livePoints = Constants.SHUTTLE_LIVEPOINTS - ((Constants.SHUTTLE_LIVES - self.__lives) * 20)
            avg.Player.get().clearInterval(self.__repairTimer)
            self.__repairTimer = None
            self.__inRepairMode = False
            self.__sImg.size = self.size
            self.__sImg.href = "resources/textures/shuttle/player"+ (self.__actor.getActorID()).__str__() +"_default.png"
            for b in self.__actor.getLevel().balls:
                    if b.getActor() == self.__actor.getActorID():
                        b.continueBall();
            self.__redAlertPlayed = False
            
        self.__updateLiveBeam()
                
    def __updateLiveBeam(self):
        self.__liveBeam.size = (self.__liveBeam.size.x, self.__livePoints)
        if (self.__livePoints <= 140):
            self.__liveBeam.fillcolor = "FFFF00"
        if (self.__livePoints <= 80):
            if self.__redAlertPlayed == False:
                self.__redAlertPlayed = True
                self.__redAlert.play()
            self.__liveBeam.fillcolor = "FF7200"
        if (self.__livePoints <= 40):
            self.__liveBeam.fillcolor = "FF0000"
        
    def __moveNodeToTop(self, node):
        parent = node.getParent()
        parent.reorderChild(node, parent.getNumChildren() - 1)

    def __moveNodeOnScreen(self, node):
        center = node.pos + node.size/2
        if center.x < 0:
            node.pos = (-node.size.x/2, node.pos.y)
        if center.x > self.__actor.size.x:
            node.pos = (self.__actor.size.x-node.size.x/2, node.pos.y)
        if center.y < 0:
            node.pos = (node.pos.x, -node.size.y/2)
        if center.y > self.__actor.size.y:
            node.pos = (node.pos.x, self.__actor.size.y-node.size.y/2)
        
        pivotx = 0
        pivoty = 0
        
        if (self.__leftCircle.getAbsPos(self.__leftCircle.pos).y > self.__rightCircle.getAbsPos(self.__rightCircle.pos).y):
            if (self.__leftCircle.getAbsPos(self.__leftCircle.pos).x < self.__rightCircle.getAbsPos(self.__rightCircle.pos).x):
                pivotx = self.__leftCircle.getAbsPos(self.__leftCircle.pos).x + (self.__rightCircle.getAbsPos(self.__rightCircle.pos).x - self.__leftCircle.getAbsPos(self.__leftCircle.pos).x) / 2
            else:
                pivotx = self.__rightCircle.getAbsPos(self.__rightCircle.pos).x + (self.__leftCircle.getAbsPos(self.__leftCircle.pos).x - self.__rightCircle.getAbsPos(self.__rightCircle.pos).x) / 2
            pivoty = self.__rightCircle.getAbsPos(self.__rightCircle.pos).y + (self.__leftCircle.getAbsPos(self.__leftCircle.pos).y - self.__rightCircle.getAbsPos(self.__rightCircle.pos).y) / 2
        else:
            if (self.__leftCircle.getAbsPos(self.__leftCircle.pos).x < self.__rightCircle.getAbsPos(self.__rightCircle.pos).x):
                pivotx = self.__leftCircle.getAbsPos(self.__leftCircle.pos).x + (self.__rightCircle.getAbsPos(self.__rightCircle.pos).x - self.__leftCircle.getAbsPos(self.__leftCircle.pos).x) / 2
            else:
                pivotx = self.__rightCircle.getAbsPos(self.__rightCircle.pos).x + (self.__leftCircle.getAbsPos(self.__leftCircle.pos).x - self.__rightCircle.getAbsPos(self.__rightCircle.pos).x) / 2
            pivoty = self.__leftCircle.getAbsPos(self.__leftCircle.pos).y + (self.__rightCircle.getAbsPos(self.__rightCircle.pos).y - self.__leftCircle.getAbsPos(self.__leftCircle.pos).y) / 2
        
        self.__body[0].position = (pivotx * Constants.PIXELS2METERS, pivoty * Constants.PIXELS2METERS)
        self.__body[1].position = ((self.__leftCircle.getAbsPos(self.__leftCircle.pos).x) * Constants.PIXELS2METERS, (self.__leftCircle.getAbsPos(self.__leftCircle.pos).y) * Constants.PIXELS2METERS)
        self.__body[2].position = ((self.__rightCircle.getAbsPos(self.__rightCircle.pos).x) * Constants.PIXELS2METERS, (self.__rightCircle.getAbsPos(self.__rightCircle.pos).y) * Constants.PIXELS2METERS)
        self.__body[0].angle = self.angle
     
    def __bonusTimeout(self):
        avg.Player.get().clearInterval(self.__bonusTimer)
        if self.__sImgBonus is not None:
            self.__sImgBonus.href = ""  
        self.__BONUS_INVINCIBLE = False
        self.__isInvincible = False
        self.__BONUS_SLIME = False
        self.__BONUS_FIRE = False
        
    def bonus(self, itemId):
        if self.__inRepairMode:
            return
        
        if itemId == Constants.ITEM_INVINCIBLE:
            self.__BONUS_INVINCIBLE = True
        
    def __onDetected(self, event):
        if self.__inRepairMode:
            return
        self.__moveNodeToTop(self)

    def __onMove(self, transform):
        if self.__inRepairMode:
            return
        
        if self.__ignoreScale:
            transform.scale = 1
        if self.__ignoreRotation:
            transform.rot = 0
        transform.moveNode(self)
        self.__moveNodeOnScreen(self)

    def __onUp(self, transform):
        pass
    
    def destroy(self):
        self.__recognizer.enable(False)
        self.__recognizer = None
        if self.__onFrameHandler:
            avg.Player.get().clearInterval(self.__onFrameHandler)
        self.__onFrameHandler = None

        if self.__bonusTimer:
            avg.Player.get().clearInterval(self.__bonusTimer)
        self.__bonusTimer = None
        
        if self.__repairTimer:
            avg.Player.get().clearInterval(self.__repairTimer)            
        self.__repairTimer = None
        self.__actor.getLevel().world.DestroyBody(self.__body[0]) 
        self.__actor.getLevel().world.DestroyBody(self.__body[1]) 
        self.__actor.getLevel().world.DestroyBody(self.__body[2])
        self.__body = None
        
        self.__centerRect.active = False
        self.__centerRect.unlink(True)
        self.__centerRect = None
        self.__leftCircle.active = False
        self.__leftCircle.unlink(True)
        self.__leftCircle = None
        self.__rightCircle.active = False
        self.__rightCircle.unlink(True)
        self.__rightCircle = None
        self.__sImg.active = False
        self.__sImg.unlink(True)
        self.__sImg = None
        self.__sImgBonus.active = False
        self.__sImgBonus.unlink(True)
        self.__sImgBonus = None
        
        self.__liveBeam.active = False
        self.__liveBeam.unlink(True)
        self.__liveBeam = None
            
        self.__pointsText.active = False
        self.__pointsText.unlink(True)
        self.__pointsText = None
        self.__pointsNode.active = False
        self.__pointsNode.unlink(True)
        self.__pointsNode = None
        
        self.__livePointsText.active = False
        self.__livePointsText.unlink(True)
        self.__livePointsText = None
        self.__liveText.active = False
        self.__liveText.unlink(True)
        self.__liveText = None
        self.__livePointsNode.active = False
        self.__livePointsNode.unlink(True)
        self.__livePointsNode = None

        self.__destrcution.unlink(True)
        self.__destrcution = None
        
        self.__redAlert.unlink(True)
        self.__redAlert = None
        
        self.__asteroidSound.unlink(True)
        self.__asteroidSound = None

        self.active = False
        self.unlink(True)
            
    def resetProperties(self): 
        self.__bonusTimeout()
        
    def inRepairMode(self):
        return self.__inRepairMode
    
class Actor(avg.DivNode):

    def __init__(self, parent, actorId, **kwargs):
        super(Actor, self).__init__(**kwargs)
        
        if parent:
            parent.appendChild(self)
            self.__level = parent
        
        #TODO: Make FIFO list for bonus
        self.__bonus = -1
        self.__bonus_addBalls = 0
        self.__actorId = actorId

        self.score = 0
        self.shuttles = []
        self.__removeShuttles = []
        
        if actorId == 0:
            self.pos = (0, 0)
        else:
            self.pos = (parent.size.x / 4 * 3, 0)
        self.size = (parent.size.x / 4, parent.size.y)
        
        self.__onFrameHandler = avg.Player.get().setOnFrameHandler(self.__update)
        
    def __update(self):
        if self.__bonus >= 0:
            self.handleBonus(self.__bonus)
            self.__bonus = -1
        self.__clear()
        
        #Extra Ball     
        while self.__bonus_addBalls > 0:
            if self.__actorId == 0:
                b = Ball(parent=self.__level, actorId=self.__actorId, position=(320 + 10, 380), additional=True)
            else:
                b = Ball(parent=self.__level, actorId=self.__actorId, position=(1280 - 320 - 40, 420), additional=True)
            self.__level.balls.append(b)
            self.__bonus_addBalls -= 1
          
    def getLevel(self):
        return self.__level
            
    def __clear(self):
        for shuttle in self.__removeShuttles:
            shuttle.destroy()
            self.shuttles.remove(shuttle)
        del self.__removeShuttles[:]
        self.__removeShuttles = []
        
    def increasePoints(self, points):
        if self.shuttles.__len__() == 1 and self.shuttles[0].inRepairMode():
            return
        
        self.score += points
        for s in self.shuttles:
            s.setPoints(self.score)
        
    def decreasePoints(self, points):
        if self.shuttles.__len__() == 1 and self.shuttles[0].inRepairMode():
            return
        
        if (self.score - points > 0):
            self.score -= points
            for s in self.shuttles:
                s.setPoints(self.score)
            
    def decreaseLive(self, live):
        for shuttle in self.shuttles:
            shuttle.decreaseLive(live)
        
    def CreateShuttle(self, shuttle):
        x = eval("float("+shuttle.getAttribute("x")+")")
        y = eval("float("+shuttle.getAttribute("y")+")")
        width = eval("float("+shuttle.getAttribute("width")+")")
        height = eval("float("+shuttle.getAttribute("height")+")")
        s = Shuttle(parent=self, position=(x, y), size=(width, height))
        self.shuttles.append(s)    
        
    def DestroyShuttle(self, shuttle):
        self.__removeShuttles.append(shuttle)
        
    def destroy(self):
        if self.__onFrameHandler:
            avg.Player.get().clearInterval(self.__onFrameHandler)
        self.__onFrameHandler = None
        
        for shuttle in self.shuttles:
            self.DestroyShuttle(shuttle)
            
        self.__clear()

        self.active = False
        self.unlink(True)
        
    def handleBonus(self, itemId):
        #Reset
        if itemId == Constants.ITEM_NORMALIZE:
            for b in self.__level.balls:
                if b.getActor() == self.__actorId:
                    b.bonus(itemId)
            for s in self.shuttles:
                s.bonus(itemId)
        
        #Ball Things
        if itemId == Constants.ITEM_SPEED or itemId == Constants.ITEM_SLOW:
            for ball in self.__level.balls:
                if(ball.getActor() == self.__actorId):
                    ball.bonus(itemId)

        if itemId == Constants.ITEM_FREEZE:
            for ball in self.__level.balls:
                if not (ball.getActor() == self.__actorId):
                    ball.bonus(itemId)

        elif itemId == Constants.ITEM_METEOR:
            i = 1
            up = randint(Constants.METEOR_MAX_COUNT/2, Constants.METEOR_MAX_COUNT)
            while i <= up:
                m = Meteor(parent=self.__level,
                           actorId=self.__actorId
                           )
                self.__level.events.append(Level.Event(parent=self.__level, node=m, maxInterval=1000, duration=8000, loop=False))
                i+=1

        #ActorStuff 
        elif itemId == Constants.ITEM_DUALBALL:
            self.__bonus_addBalls += 1
            
        #Plus 10
        elif itemId == Constants.ITEM_PLUS10:
            self.increasePoints(10)
            
        #LevelThings
        elif itemId == Constants.ITEM_SWITCH:
            level = self.__level
            level.bonus(itemId)
            
        #Shuttle Things
        elif itemId == Constants.ITEM_INVINCIBLE:
            for s in self.shuttles:
                s.bonus(itemId)
                        
    def resetProperties(self):
        pass
        
    def getActorID(self):
        return self.__actorId  
    
    def setBonus(self, bonusId):
        self.__bonus = bonusId
        
class Ball(avg.DivNode):
     
    __BONUS_FREEZE = False
    __BONUS_NORMALIZE = False
    __BONUS_SPEEDUP = False
    __BONUS_SLOWDOWN = False
    
    __isFrozen = False
    
    def __init__(self, parent, position, actorId, speed=None, radius=None, angle = 0.0, additional = False,  **kwargs):
        super(Ball, self).__init__(**kwargs)
        
        #Init parent
        if parent:
            parent.appendChild(self)
            self.__level = parent

        self.__lastVelocity = None
        self.__lastBodyPosition = None
        self.__recognizer = None
            
        #Set defaults if not given
        if speed is None:
            self.__speed = Constants.BALL_DEFAULT_SPEED
        else:
            self.__speed = speed
        
        if radius is None:
            self.__radius = Constants.BALL_DEFAULT_RADIUS
        else:
            self.__radius = radius

        self.size=(self.__radius * 2, self.__radius * 2)
        self.pos = (position[0] - self.__radius, position[1] - self.__radius)
        
        self.__actorId = actorId
        self.__additional = additional
        self.__sImgTrail = avg.ImageNode(angle=angle, gamma = (0.8, 0.8, 0.8), pos=(-4, -4), size=(self.size.x + 7, self.size.y + 7), parent=self, href="resources/textures/ball/overlay_default.png")     
        self.__ballImg = avg.ImageNode(size=self.size, gamma = (0.8, 0.8, 0.8), parent=self, href="resources/textures/ball/ball.png")
        
        if self.__actorId == 0:
            self.__ballImg.intensity = (1.0, 1.0, 0.0)
            if angle is not None: 
                angle = 0.0
        else:
            self.__ballImg.intensity = (1.0, 0.0, 0.0)
            if angle is not None: 
                angle = math.pi
                
        self.__sImgBonus = avg.ImageNode(pos=(-6,-6), size=(self.size.x + 10, self.size.y + 10), parent=self)   
        
        #sounds
        self.__collision_sound = avg.SoundNode(parent=self, href="resources/sound/ball_hit.mp3", loop=False, volume=Constants.BALL_VOL)
        
        # add physical body
        self.__body = self.__level.world.Circle(position=position, radius=self.__radius)
        self.__body.userData = self
        self.__body.type = b2_dynamicBody
        self.__body.fixtures[0].filterData.maskBits = 175
        self.__body.fixtures[0].filterData.categoryBits = 128
        self.bullet = True
        self.__body.linearVelocity = b2Vec2(math.cos(angle) * self.__speed, math.sin(angle) * self.__speed)
        
        self.__updateColor()
        self.__bonusTimer = None
        self.__onFrameHandler = avg.Player.get().setOnFrameHandler(self.__update)
    
    def getActor(self):
        return self.__actorId
    
    def getBody(self):
        return self.__body
    
    def setActorID(self, newID):
        self.__actorId = newID
        self.__updateColor()
    
    def __bonusTimeout(self):
        avg.Player.get().clearInterval(self.__bonusTimer)
        self.__sImgBonus.href = ""
        self.__BONUS_FREEZE = False
        self.__isFrozen = False
        self.continueBall()
    
    def __update(self):
        if self.__isFrozen == True or self.isInRepairMode() == True:
            return 

        if (self.__isFrozen == False and self.isInRepairMode() == False and self.__body.linearVelocity.length == 0):
            self.resetPosition()

        self.pos = (self.__body.position.x * Constants.METERS2PIXELS - self.__radius, self.__body.position.y * Constants.METERS2PIXELS - self.__radius)
    
        #check if out of world
        if (self.pos.x < (-20)) or (self.pos.x > (self.__level.size.x + 20)) or (self.pos.y < (-20)) or (self.pos.y > (self.__level.size.y + 20)) :
            if self.__additional == False:
                #I am additional
                myBalls = 0
                for b in self.__level.balls:
                    if b.getActor() == self.__actorId:
                        myBalls += 1
                        
                #This actors has " + myBalls.__str__() + " balls
                if myBalls == 1:
                    #So I am the only one and kill the shuttle
                    self.resetPosition()
                    self.__level.actors[self.__actorId].shuttles[0].kill()
                elif myBalls > 1:
                    #There are more and I give my status to someone else...
                    for b in self.__level.balls:
                        if b.getActor() == self.__actorId and b.__additional == True:
                            #Found someone
                            b.__additional = False
                            self.__level.DestroyBall(self)
                            return
            else:
                self.__level.DestroyBall(self)
                return

        if self.__BONUS_SPEEDUP == True:
            self.__BONUS_SPEEDUP = False
            self.__speed = Constants.BALL_FAST_SPEED
        elif self.__BONUS_SLOWDOWN == True:
            self.__BONUS_SLOWDOWN = False
            self.__speed = Constants.BALL_SLOW_SPEED
        elif self.__BONUS_NORMALIZE == True:
            self.__BONUS_NORMALIZE = False
            if self.__additional:
                self.__level.DestroyBall(self)
            else:
                self.resetProperties()
        elif self.__BONUS_FREEZE == True:
            self.__BONUS_FREEZE = False
            if self.__isFrozen == True:
                avg.Player.get().clearInterval(self.__bonusTimer)
            self.__sImgBonus.href = "resources/textures/ball/frozen.png"

            self.pauseBall()
            self.__isFrozen = True

            self.__bonusTimer = avg.Player.get().setInterval(10000, self.__bonusTimeout)
            
            if self.__recognizer:
                self.__recognizer.enable(False)
                self.__recognizer = None
                
            self.__recognizer = ui.HoldRecognizer(
                node=self.__sImgBonus, 
                delay=1500,
                detectedHandler=self.__onMelt
                )

        self.__updateSpeed()
        
    def isFrozen(self):
        return self.__isFrozen
        
    def onBeginContact(self, o):
        #play sound
        if (isinstance(o, Shuttle)):
            self.__collision_sound.stop()
            self.__collision_sound.play()
    
    def onEndContact(self, o):
        if(isinstance(o, Level.Wall)):
            if o.getActor() is not None and o.getActor() == self.__actorId:
                self.__level.actors[self.__actorId].decreaseLive(Constants.BALL_WALL_MINUS_LIVE)
                
        #Update speed
        if self.__isFrozen == False and self.isInRepairMode() == False: 
            self.__updateSpeed()
        else:
            self.__body.linearVelocity = b2Vec2(0, 0)
    
    def __onMelt(self, event):
        self.__recognizer.enable(False)
        self.__recognizer = None
        self.__bonusTimeout() 
           
    def pauseBall(self):     
        if self.__body.linearVelocity.x != 0 and self.__body.linearVelocity.y != 0:
            self.__lastVelocity = self.__body.linearVelocity 
        self.__lastBodyPosition = self.__body.position
        self.__body.awake = False
        
    def continueBall(self):
        self.__body.awake = True
        if self.__lastBodyPosition is not None:
            self.__body.position = self.__lastBodyPosition
        if self.__lastVelocity is not None:
            self.__body.linearVelocity = self.__lastVelocity
           
    def bonus(self, itemId):
        if itemId == Constants.ITEM_FREEZE:
            self.__BONUS_FREEZE = True
        elif itemId == Constants.ITEM_SPEED:
            self.__BONUS_SPEEDUP = True
        elif itemId == Constants.ITEM_SLOW:
            self.__BONUS_SLOWDOWN = True
        elif itemId == Constants.ITEM_NORMALIZE:
            self.__BONUS_NORMALIZE = True
    
    def __updateColor(self):
        if self.__actorId == 0:
            self.__ballImg.intensity = (1.0, 1.0, 0.0)
            self.__sImgTrail.intensity = (1.0, 1.0, 0.0)
        else:
            self.__ballImg.intensity = (1.0, 0.0, 0.0)    
            self.__sImgTrail.intensity = (1.0, 0.0, 0.0)
        
    def isInRepairMode(self):
        actor = self.__level.actors[self.__actorId]
        if actor.shuttles.__len__() == 1 and actor.shuttles[0].inRepairMode():
            return True
        else:
            return False
    
    def __updateSpeed(self):   
        tmpSpeed2 = math.sqrt(math.pow(self.__body.linearVelocity.x,2) + math.pow(self.__body.linearVelocity.y,2))
        if tmpSpeed2 > 0:
            self.__tmpDirection = avg.Point2D(self.__body.linearVelocity.x / tmpSpeed2, self.__body.linearVelocity.y / tmpSpeed2)
            self.__body.linearVelocity = b2Vec2(self.__tmpDirection.x * self.__speed, self.__tmpDirection.y * self.__speed) 
            
            #Avoid linewalks
            if self.__body.linearVelocity.x == 0 or self.__body.linearVelocity.y == 0 :
                center = self.__level.size/2
                myCenter = avg.Point2D(self.pos.x + self.__radius, self.pos.y + self.__radius)
                addForce = b2Vec2(center.x - myCenter.x,  center.y - myCenter.y) * Constants.WALL_WALK_FIX
                self.__body.ApplyForce(addForce, self.__body.position)
            
    def resetPosition(self):
        if self.__actorId == 0:
            angle = 0.0
            self.__body.position = ((320 + self.__radius) * Constants.PIXELS2METERS, 400 * Constants.PIXELS2METERS)
        else:
            angle = math.pi
            self.__body.position = ((1280 - 320 - self.__radius) * Constants.PIXELS2METERS, 400 * Constants.PIXELS2METERS)

        self.__body.linearVelocity = b2Vec2(math.cos(angle) * self.__speed, math.sin(angle) * self.__speed)
        self.__body.awake = True
        
    def resetProperties(self): 
        self.__speed = Constants.BALL_DEFAULT_SPEED
        self.__BONUS_FREEZE = False
        self.__BONUS_SPEEDUP = False
        self.__BONUS_SLOWDOWN = False
        self.__BONUS_NORMALIZE = False
            
    def destroy(self):
        if self.__onFrameHandler:
            avg.Player.get().clearInterval(self.__onFrameHandler)
        self.__onFrameHandler = None
        
        if self.__bonusTimer:
            avg.Player.get().clearInterval(self.__bonusTimer)
        self.__bonusTimer = None
        
        self.__level.world.DestroyBody(self.__body)
        self.__body = None
        self.__sImgBonus.active = False
        self.__sImgBonus.unlink(True)
        self.__sImgBonus = None
        self.__ballImg.active = False
        self.__ballImg.unlink(True)
        self.__ballImg = None
        self.__collision_sound.unlink(True)
        self.__collision_sound = None
        self.active = False
        self.unlink(True)